#include "cvxMatch.h"
#include "cvxImage.h"
#include <map>

using std::map;
using cvx::Bw8UImage;

CvxMatch::CvxMatch(int threshold, int desireNumber, int verticalMax)
{	
	assert(threshold > 0);
	assert(desireNumber > 50);
	m_pExtractor = new BriefDescriptorExtractor(16);
	m_pFast = new FastFeatureDetector(threshold, true);
	m_pDetector = new GridAdaptedFeatureDetector(m_pFast, desireNumber*4, 4, 4);	
	m_pMatcher = new BFMatcher(NORM_HAMMING);

	assert(m_pExtractor);
	assert(m_pFast);
	assert(m_pDetector);
	assert(m_pMatcher);

	m_threshold = threshold;
	m_desiredNumber = desireNumber;
	m_verticalDispartyMax = verticalMax;
}
CvxMatch::~CvxMatch()
{	
	if (m_pExtractor)
	{
		delete m_pExtractor, m_pExtractor = NULL;
	}
	if (m_pDetector)
	{
		delete m_pDetector, m_pDetector = NULL;
	}
	if (m_pMatcher)
	{
		delete m_pMatcher, m_pMatcher = NULL;
	}

}

void CvxMatch::configure(int threshold, int matchNumber)
{
//	printf("\nthreshodl = %d match number = %d\n", threshold, matchNumber);
	if (m_pDetector)
	{
		delete m_pDetector, m_pDetector = NULL;
	}

	if (threshold != m_threshold)
	{	
		m_pFast = new FastFeatureDetector(threshold, true);
		assert(m_pFast);			
	}

	m_pDetector = new GridAdaptedFeatureDetector(m_pFast, matchNumber*4, 4, 4);
	assert(m_pDetector);

	m_threshold = threshold;
}

bool CvxMatch::particalMatch(const Mat &imageA, const Mat &imageB, vector<Point2f> &ptA, vector<Point2f> &ptB, 
							 const cv::Rect &roi)
{
	/*
		match left and right image in an rectangle area
	*/
	if( imageA.empty() || imageB.empty())
		return false;
	CV_Assert(imageA.size() == imageB.size());
	if (imageA.type() != CV_8UC1 || imageB.type() != CV_8UC1)
		return false;

	Mat subLeftImage =  Mat(imageA, roi);
	Mat subRightImage = Mat(imageB, roi);

	return this->match(subLeftImage, subRightImage, ptA, ptB);
}

bool CvxMatch::match(const Mat &imageA, const Mat &imageB, vector<Point2f> &ptA, vector<Point2f> &ptB)
{
	if( imageA.empty() || imageB.empty())
		return false;
	CV_Assert(imageA.size() == imageB.size());
	if (imageA.type() != CV_8UC1 || imageB.type() != CV_8UC1)
		return false;
	ptA.clear();
	ptB.clear();

	assert(m_pExtractor);
	assert(m_pFast);
	assert(m_pDetector);
	assert(m_pMatcher);

	//detect key points
	vector<KeyPoint> keypoints1, keypoints2;
	m_pDetector->detect(imageA, keypoints1);
	m_pDetector->detect(imageB, keypoints2);

//	printf("key point number is %d %d\n", keypoints1.size(), keypoints2.size());


	// computing descriptors
	Mat descriptors1, descriptors2;
	m_pExtractor->compute(imageA, keypoints1, descriptors1);
	m_pExtractor->compute(imageB, keypoints2, descriptors2);

	// matching descriptors
	vector<DMatch> matches;
	m_pMatcher->match(descriptors1, descriptors2, matches);

	//use fundamental map to extract outliers
	vector<Point2f> pts1(matches.size());
	vector<Point2f> pts2(matches.size());
	for (int i = 0; i<matches.size(); ++i)
	{
		int i1 = matches[i].queryIdx;
		int i2 = matches[i].trainIdx;
		pts1[i] = keypoints1[i1].pt;
		pts2[i] = keypoints2[i2].pt;
	}	

	Mat inlierMat;
	Mat F = cv::findFundamentalMat(pts1, pts2, FM_RANSAC, 1, 0.99, inlierMat);
	if (F.empty())
	{
		return false;		
	}

	vector<DMatch> matchInlier;
	for (int i = 0; i<pts1.size(); ++i)
	{
		if (inlierMat.at<unsigned char>(i, 0) != 0)
		{
			matchInlier.push_back(matches[i]);
			int i1 = matches[i].queryIdx;
			int i2 = matches[i].trainIdx;
			if (fabs(keypoints1[i1].pt.y - keypoints2[i2].pt.y) < m_verticalDispartyMax)
			{
				ptA.push_back(keypoints1[i1].pt);
				ptB.push_back(keypoints2[i2].pt);
			}			
		}
	}

	//adjust threshold	
	if (ptA.size() > m_desiredNumber + 20)
	{
		tooMany();
	}
	else if (ptA.size() < m_desiredNumber - 20)
	{
		tooFew();
	}

	return true;
}

bool CvxMatch::fastMatch(const Mat &img1, const Mat &img2, vector<Point2f> &ptA, vector<Point2f> &ptB, 
						 const int maxVDisparity)
{
	//parameter check
	assert(!img1.empty() && !img2.empty());
	assert(img1.rows == img2.rows && img1.cols == img2.cols);

	Mat gray1, gray2;
	if (img1.channels() == 3){		cv::cvtColor(img1, gray1, CV_BGR2GRAY);	}
	else 	{		gray1 = img1;	}

	if (img2.channels() == 3)	{		cv::cvtColor(img2, gray2, CV_BGR2GRAY);	}
	else	{		gray2 = img2;	}
	
	int h = img1.rows;	
	int maxTotalKeypoints=1000;
	int gridRows=4;
	int gridCols=4;

	// detecting keypoints threshold 9 to 12
	GridAdaptedFeatureDetector detector(new FastFeatureDetector(10, true), maxTotalKeypoints, 4, 4);
	vector<KeyPoint> keypoints1, keypoints2;
	detector.detect(gray1, keypoints1);
	detector.detect(gray2, keypoints2);


	//filter keypoints by brief descriptor
	BriefDescriptorExtractor extractor(32);
	Mat descriptors1, descriptors2;
	extractor.compute(gray1, keypoints1, descriptors1);  //will remove key point
	extractor.compute(gray2, keypoints2, descriptors2);

	//haimming distance matching between brief descriptors	
	vector<DMatch> hmatches;
	CvxMatch::haimmingMatch(keypoints1, keypoints2, descriptors1, descriptors2, maxVDisparity, h, hmatches);

	//zncc match	
	int w_len = 9;
	vector<DMatch> znccmatches;	
	bool isFound = CvxMatch::zncc(keypoints1, keypoints2, gray1, gray2, maxVDisparity, 0.8, w_len, znccmatches);	

	//matches that in both haimming code and zncc
	vector<DMatch> matchCombine;
	vector<int> matchIdx(keypoints1.size(), -1);

	
	//record zncc match result
	for (int i = 0; i<znccmatches.size(); ++i){		
		matchIdx[znccmatches[i].queryIdx] =  znccmatches[i].trainIdx;
	}
	for (int i = 0; i<hmatches.size(); ++i)
	{
		int i1 = hmatches[i].queryIdx;
		int i2 = hmatches[i].trainIdx;
		if (matchIdx[i1] == i2)
		{
			DMatch aMatch;
			aMatch.queryIdx = i1;
			aMatch.trainIdx = i2;
			matchCombine.push_back(aMatch);				
		}		
	}

	//fundamental matrix filter
	vector<Point2f> pts1;
	vector<Point2f> pts2;
	for (int i = 0; i<matchCombine.size(); ++i)
	{
		pts1.push_back(keypoints1[matchCombine[i].queryIdx].pt);
		pts2.push_back(keypoints2[matchCombine[i].trainIdx].pt);		
	}

	//using fundamental matrix to filter out outliers
	Mat inlierMat;
	Mat F = cv::findFundamentalMat(pts1, pts2, FM_RANSAC, 1, 0.99, inlierMat);
	if (F.empty())
	{
		return false;
	}

	float avg_x_dis = 0; //check average distance constraint
	vector<DMatch> fmatch;
	for (int i = 0; i<pts1.size(); ++i)
	{
		if (inlierMat.at<unsigned char>(i, 0) != 0)
		{
			fmatch.push_back(matchCombine[i]);
			int i1 = matchCombine[i].queryIdx;
			int i2 = matchCombine[i].trainIdx;
			avg_x_dis += fabs(keypoints1[i1].pt.x - keypoints2[i2].pt.x);
		}		
	}
	avg_x_dis /= fmatch.size();

	//update matched pts set
	ptA.clear();
	ptB.clear();
	for (int i = 0; i<fmatch.size(); ++i)
	{
		int i1 = fmatch[i].queryIdx;
		int i2 = fmatch[i].trainIdx;
		float dis = fabs(keypoints1[i1].pt.x - keypoints2[i2].pt.x);
		if (dis < avg_x_dis *3)
		{
			ptA.push_back(keypoints1[i1].pt);
			ptB.push_back(keypoints2[i2].pt);	
		}			
	}
	if (ptA.size() < 100)
	{
		return false;
	}
	return true;
}

bool CvxMatch::SIFTmatch(const Mat &img1, const Mat &img2, vector<Point2f> &ptA, vector<Point2f> &ptB, bool inAPlane)
{
	printf("unfinished CvxMatch::SIFTmatch\n");
	assert(0);
	//parameter check
	assert(!img1.empty() && !img2.empty());
	assert(img1.rows == img2.rows && img1.cols == img2.cols);

	Mat gray1, gray2;
	if (img1.channels() == 3){		cv::cvtColor(img1, gray1, CV_BGR2GRAY);	}
	else 	{		gray1 = img1;	}

	if (img2.channels() == 3)	{		cv::cvtColor(img2, gray2, CV_BGR2GRAY);	}
	else	{		gray2 = img2;	}
	
	int h = img1.rows;	
	int maxTotalKeypoints=1000;
	int gridRows=4;
	int gridCols=4;

	// detecting keypoints threshold 9 to 12
	GridAdaptedFeatureDetector detector(new cv::SiftFeatureDetector(), maxTotalKeypoints, 4, 4);
	vector<KeyPoint> keypoints1, keypoints2;
	detector.detect(gray1, keypoints1);
	detector.detect(gray2, keypoints2);


	//filter keypoints by SIFT descriptor
	SiftDescriptorExtractor extractor(32);
	Mat descriptors1, descriptors2;
	extractor.compute(gray1, keypoints1, descriptors1);  //will remove key point
	extractor.compute(gray2, keypoints2, descriptors2);

	//FlannBasedMatcher matcher;
	//std::vector< DMatch > matches;
//	matcher.match( descriptors_1, descriptors_2, matches );
	return true;
}

bool CvxMatch::findHomographyInliner(vector<Point2f> &ptA, vector<Point2f> &ptB, int reprojThreshold)
{
	assert(ptA.size() == ptB.size());

	Mat H;
	Mat inlierMat;
	H = cv::findHomography(ptA, ptB, CV_RANSAC, reprojThreshold, inlierMat);

	if(H.empty())  {return false;}
	
	vector<Point2f> retPtA;
	vector<Point2f> retPtB;
	for(int i = 0; i<ptA.size(); i++)
	{
		if(inlierMat.at<unsigned char>(i, 0) != 0)
		{
			retPtA.push_back(ptA[i]);
			retPtB.push_back(ptB[i]);
		}
	}

	ptA = retPtA;
	ptB = retPtB;
	return true;
}

bool CvxMatch::showMatchedImage(const Mat &leftImage, const Mat &rightImage, const vector<Point2f> &ptA, const vector<Point2f> &ptB)
{
	int h = leftImage.rows;
	int w = leftImage.cols;

	Mat showImage = cv::Mat::zeros(h, w*2, CV_8UC3);
	leftImage.copyTo(showImage(Rect(0, 0, w, h)));
	rightImage.copyTo(showImage(Rect(w, 0, w, h)));

	assert(ptA.size() == ptB.size());
	for(int i = 0; i<ptA.size(); i++)
	{
		int r = rand()%256;
		int g = rand()%256;
		int b = rand()%256;

		cv::Point p1(ptA[i]);
		cv::Point p2(ptA[i].x + w, ptA[i].y);

		cv::line(showImage, p1, p2, Scalar(b, g, r));
		cv::circle(showImage, p1, 2, Scalar(b, g, r));
		cv::circle(showImage, p2, 2, Scalar(b, g, r));
	}
	cv::imshow("matched image", showImage);
	return true;
}

static float zncc_value(float *p1, float *p2, int len)
{
	float v1 = 0, v2 = 0, v3 = 0;
	for (int i = 0; i<len; ++i){
		v1 += p1[i] * p2[i];
		v2 += p1[i] * p1[i];
		v3 += p2[i] * p2[i];
	}
	return v1/sqrt(v2*v3);
}

bool CvxMatch::zncc(const vector<KeyPoint> &kps1, const vector<KeyPoint> &kps2, 
					const Mat &img1, const Mat &img2, int v_d_th, float zncc_th,
					int w_len, vector<DMatch> &matches)
{
	//check input parameters
	assert(kps1.size() > 50);
	assert(kps2.size() > 50);
	assert(!img1.empty() && img1.channels() == 1 && img1.depth() == CV_8U);
	assert(!img2.empty() && img2.channels() == 1 && img2.depth() == CV_8U);
	assert(img1.rows == img2.rows && img1.cols == img2.cols);
<<<<<<< .mine
	//assert(v_d_th >=0 && v_d_th <= 5);
=======
	assert(v_d_th >=0 && v_d_th <= 50);
>>>>>>> .r258
	assert(zncc_th > 0.1);
	assert(w_len > 1 && w_len%2 == 1);

	if (img1.channels() != 1 || img2.channels() != 1)
	{
		cout<<"input of zncc must be gray image."<<endl;
		return false;
	}

	int h = img1.rows;
	int w = img1.cols;
	int vLen = w_len*w_len;
	int half_w = w_len/2;
	//zncc vector
	vector<float> data1(kps1.size() * (vLen+1), 0);  //zero mean intensity vector and zncc inital value
	vector<int>   crs1(kps1.size(), -1);             //-1 no match, -2 boundary points no match
	map<int, vector<int>> idxMap1;                   //index map hash table for points position

	vector<float> data2(kps2.size() * (vLen+1), 0);
	vector<int>   crs2(kps2.size(), -1);
	map<int, vector<int>> idxMap2;

	//left image
	Bw8UImage img_1(&img1);	 
	for (int i = 0; i<kps1.size(); ++i)
	{
		int p_x = cvRound(kps1[i].pt.x);
		int p_y = cvRound(kps1[i].pt.y);
		//boundary pts, no match
		if (p_x-w_len < 0 || p_x+w_len >= w || p_y-w_len < 0 || p_y+w_len >= h) { crs1[i] = -2;	}

		else
		{
			float *pdata = &data1[i*(vLen+1)];
			for (int y = -half_w, m = 0; y<=half_w; ++y){
				for (int x = -half_w; x<=half_w; ++x, m++){pdata[m] = img_1[p_y+y][p_x+x];}
			}					
			float s = 0;
			for (int j =0 ; j<vLen; ++j) {s += pdata[j];}
				
			s /= vLen;  //average value;
			for (int j = 0; j<vLen; ++j){pdata[j] -= s;}

			idxMap1[p_y].push_back(i); //store the index
		}
	}

	//right image
	{
		Bw8UImage img_2(&img2);	 
		for (int i = 0; i<kps2.size(); ++i)
		{
			int p_x = cvRound(kps2[i].pt.x);
			int p_y = cvRound(kps2[i].pt.y);
			//boundary pts, no match
			if (p_x-w_len < 0 || p_x+w_len >= w || p_y-w_len < 0 || p_y+w_len >= h)	{ crs2[i] = -2;	}
			else
			{
				float *pdata = &data2[i*(vLen+1)];
				for (int y = -half_w, m = 0; y<=half_w; ++y){
					for (int x = -half_w; x<=half_w; ++x, m++){pdata[m] = img_2[p_y+y][p_x+x];}
				}					
				float s = 0;
				for (int j =0 ; j<vLen; ++j) {s += pdata[j];}

				s /= vLen;  //average value;
				for (int j = 0; j<vLen; ++j){pdata[j] -= s;}

				idxMap2[p_y].push_back(i); //store the index
			}
		}
	}	
	//calculate zncc value
	for (int i = 0; i<crs1.size(); ++i)
	{
		//find minimal zncc corresponding points in the right image
		if (crs1[i] != -2)
		{
			int p_y = cvRound(kps1[i].pt.y);
			float *pData1 = &data1[i*(vLen+1)];
			for (int y = p_y - v_d_th; y<= p_y + v_d_th; ++y)
			{
				//potential corresponding sets
				if (!idxMap2[y].empty())
				{
					for (int j = 0; j<idxMap2[y].size(); ++j)
					{
						int idx2 = idxMap2[y][j];
						float *pData2 = &data2[idx2*(vLen+1)];

						float zncc_v = zncc_value(pData1, pData2, vLen);
						//update zncc_value in left image data set
						if (zncc_v > pData1[vLen])
						{
							pData1[vLen] = zncc_v;
							crs1[i] = idx2;  //
						}

						//update zncc_value in right image data set
						if (zncc_v > pData2[vLen])
						{
							pData2[vLen] = zncc_v;
							crs2[idx2] = i;
						}
					}
				}
			}

		}
	}
	//cross-consistency check
	for (int i = 0; i<crs1.size(); ++i)
	{
		int idx2 = crs1[i];
		if (idx2 != -1 && idx2 != -2)
		{						
			if (crs2[idx2] == i)
			{
				DMatch aMatch;
				aMatch.queryIdx = i;
				aMatch.trainIdx = idx2;
				matches.push_back(aMatch);
			}			
		}
	}

	return true;
}

static const uchar popCountTable[] = 
{
	0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};

//normalized haimming distance
static int cvxnormHamming(const uchar* a, const uchar* b, int n)
{
	int i = 0, result = 0;
#if 0
	if (CPU_HAS_NEON_FEATURE)
	{
		uint32x4_t bits = vmovq_n_u32(0);
		for (; i <= n - 16; i += 16) {
			uint8x16_t A_vec = vld1q_u8 (a + i);
			uint8x16_t B_vec = vld1q_u8 (b + i);
			uint8x16_t AxorB = veorq_u8 (A_vec, B_vec);
			uint8x16_t bitsSet = vcntq_u8 (AxorB);
			uint16x8_t bitSet8 = vpaddlq_u8 (bitsSet);
			uint32x4_t bitSet4 = vpaddlq_u16 (bitSet8);
			bits = vaddq_u32(bits, bitSet4);
		}
		uint64x2_t bitSet2 = vpaddlq_u32 (bits);
		result = vgetq_lane_s32 (vreinterpretq_s32_u64(bitSet2),0);
		result += vgetq_lane_s32 (vreinterpretq_s32_u64(bitSet2),2);
	}
	else
#endif
		for( ; i <= n - 4; i += 4 )
			result += popCountTable[a[i] ^ b[i]] + popCountTable[a[i+1] ^ b[i+1]] +
			popCountTable[a[i+2] ^ b[i+2]] + popCountTable[a[i+3] ^ b[i+3]];
	for( ; i < n; i++ )
		result += popCountTable[a[i] ^ b[i]];
	return result;
}

static unsigned char hamdist(unsigned char x, unsigned char y)   
{   
	    unsigned char dist = 0, val = x ^ y;     
	    // Count the number of set bits
		while(val)   
		{
			++dist;   
			val &= val - 1; 			         
		}		    
	    return dist;   
}   


bool CvxMatch::haimmingMatch(const vector<KeyPoint> &kps1, const vector<KeyPoint> &kps2, 
				   const Mat &dsp1, const Mat &dsp2, const unsigned int v_th, const int height, vector<DMatch> &matches)
{
	//check parameters
	assert(kps1.size() == dsp1.rows);
	assert(kps2.size() == dsp2.rows);
	matches.clear();

	int len = dsp1.cols;
	assert(len <= 64);

	//check format of description and haimming distance
//	const uchar* s1 = dsp1.ptr(0, 0);
//	const uchar* s2 = dsp2.ptr(0, 0);
//	int dis = cvxnormHamming(s1, s2, len);
	
	//hash map
	vector<int> dist1(kps1.size(), 512);   //initial distance value
	vector<int> crs1(kps1.size(), -1);             //-1 no match, -2 boundary points no match
	map<int, vector<int>> idxMap1;                 //index map hash table for points position

	vector<int> dist2(kps2.size(), 512);   //initial distance value
	vector<int> crs2(kps2.size(), -1);             //-1 no match, -2 boundary points no match
	map<int, vector<int>> idxMap2;                 //index map hash table for points position

	int w_len = v_th/2;
	for (int i = 0; i<kps1.size(); ++i)
	{
		int p_x = cvRound(kps1[i].pt.x);
		int p_y = cvRound(kps1[i].pt.y);
		//boundary pts, no match
		if (p_y-w_len < 0 || p_y+w_len >= height) { crs1[i] = -2;	}
		else {	idxMap1[p_y].push_back(i);} //store the index		
	}

	for (int i = 0; i<kps2.size(); ++i)
	{
		int p_x = cvRound(kps2[i].pt.x);
		int p_y = cvRound(kps2[i].pt.y);
		//boundary pts, no match
		if (p_y-w_len < 0 || p_y+w_len >= height) { crs2[i] = -2;	}
		else {	idxMap2[p_y].push_back(i);} //store the index		
	}

	//comparison
	for (int i = 0; i<crs1.size(); ++i)
	{
		//find minimal zncc corresponding points in the right image
		if (crs1[i] != -2)
		{
			int p_y = cvRound(kps1[i].pt.y);
			const unsigned char *pData1 = dsp1.ptr(i, 0);
			for (int y = p_y - v_th; y<= p_y + v_th; ++y)
			{
				//potential corresponding sets
				if (!idxMap2[y].empty())
				{
					for (int j = 0; j<idxMap2[y].size(); ++j)
					{
						int idx2 = idxMap2[y][j];
						const unsigned char *pData2 = dsp2.ptr(idx2, 0);

						int dis = cvxnormHamming(pData1, pData2, len);
						//update heimming distance in left image data set
						if (dis < dist1[i])
						{
							dist1[i] = dis;
							crs1[i] = idx2;  //
						}

						//update heimming distance in right image data set
						if (dis < dist2[idx2])
						{
							dist2[idx2] = dis;
							crs2[idx2] = i;
						}
					}
				}
			}
		}
	}

	//cross-consistency check
	for (int i = 0; i<crs1.size(); ++i)
	{
		int idx2 = crs1[i];
		if (idx2 != -1 && idx2 != -2)
		{						
			if (crs2[idx2] == i)
			{
				DMatch aMatch;
				aMatch.queryIdx = i;
				aMatch.trainIdx = idx2;
				matches.push_back(aMatch);							
			}			
		}
	}

	return true;
}

void CvxMatch::tooFew()
{
	if (m_threshold > 10)
	{
		configure(m_threshold-1, m_desiredNumber+5);
	}	
}

void CvxMatch::tooMany()
{		
	configure(m_threshold+1, m_desiredNumber-5);
}